/* Copyright 2005-2006 Tim Fennell
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.stars.util.reflect;

import javax.servlet.ServletContext;

import java.lang.annotation.Annotation;

/**
 * <p>ResolverUtil is used to locate classes that implement an interface or extend a given base
 * class. It does this in two different ways.  The first way is by accessing the
 * {@link Thread#getContextClassLoader() Context ClassLoader} and attempting to discover the set
 * of URLs that are used for classloading.  The second mechanism uses the {@link ServletContext}
 * to discover classes under {@code /WEB-INF/classes/} and jar files under {@code /WEB-INF/lib/}</p>.
 *
 * <p>The first mechanism is generally preferred since it can usually discover classes in more
 * locations, but it requires that the context class loader be a subclass of {@link URLClassLoader}.
 * Most containers use class loaders that extend URLClassloader, but not all do.  Since accessing
 * resources through the ServletContext is mandated to work in the Servlet specification this should
 * work in all containers.</p>
 *
 * <p>Since scanning all classpath entries and/or jars under {@code /WEB-INF/lib/} can take a
 * non-trivial amount of time, it is possible to filter the set of locations and packages that
 * are examined.  This is done by supplying Collections of filter patterns.  The
 * {@code locationFilters} are used to match the locations (directories, jar files, etc.) examined.
 * The {@code packageFilters} restricts the set of classes loaded by package.  In both cases a
 * simple sub-string match is used.  For example if location patterns of ["project1", project2"] are
 * supplied, you would see the following:</p>
 *
 *<pre>
 *lib/project1/dependencies/dep1.jar  -> scanned
 *lib/project3/dependencies/dep79.jar -> not scanned
 *WEB-INF/lib/project1-web.jar        -> scanned
 *WEB-INF/classes                     -> not scanned
 *lib/project2/project2-business.jar  -> scanned
 *</pre>
 *
 * <p>If no location filters are supplied, all discovered locations will be scanned for classes.
 * If no package filters are supplied, all classes discovered will be checked.</p>
 *
 * <p>At first glance it may seem redundant to provide the class type being searched for at
 * instantiation time, and again when invoking one of the {@code load()} methods.  However,
 * this allows for certain usages that would not otherwise be possible.  For example, the
 * following is used to find all collections that support ordering of some kind:</p>
 *
 *<pre>
 *ResolverUtil&lt;Collection&gt; resolver = new ResolverUtil&lt;Collection&gt;();
 *resolver.loadImplementationsFromContextClassloader(List.class);
 *resolver.loadImplementationsFromContextClassloader(SortedSet.class);
 *Set&lt;Class&lt;? extends Collection&gt;&gt; classes = resolver.getClasses();
 *</pre>
 * @author Tim Fennell
 * @param <T> 
 */
public class AnnotationResolverUtil<T,R> extends BaseResolverUtil<T,R> {    
    /**
     * Add the class designated by the fully qualified class name provided to the set of
     * resolved classes if and only if it extends/implements the parent type supplied.
     *
     * @param parentType the interface or class to add implementations or subclasses of.
     * @param fqn the fully qualified name of a class
     */
    @Override
	@SuppressWarnings("unchecked")
	protected void addIfAssignableTo(Class<? extends T> parentType, String fqn) {
        try {
        	//System.out.println("Checking to see if class '"+fqn+ "' implements "+ parentType.getName());
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');

            Class<?> type = loader.loadClass(externalName);
                                    
            Annotation[] annotations = type.getAnnotations();
            
            for (Annotation item: annotations)
            {
            	Class<?> itemz=item.annotationType();
            	if (itemz.equals(parentType))
            	{
            		implementations.add( (Class<R>) type);
            		break;
            	}
            }
            
        }
        catch (Throwable t) {
        	/*System.out.println("Could not examine class '"+ fqn+ "'"+ " due to a "+
                     t.getClass().getName()+ " with message: "+ t.getMessage());*/
        }
    }

    
}